// @ts-strict-ignore
import { useExitFormDialog } from "@dashboard/components/Form/useExitFormDialog";
import { OrderRefundDataQuery } from "@dashboard/graphql";
import useForm, { CommonUseFormResultWithHandlers, SubmitPromise } from "@dashboard/hooks/useForm";
import useFormset, { FormsetChange, FormsetData } from "@dashboard/hooks/useFormset";
import useHandleFormSubmit from "@dashboard/hooks/useHandleFormSubmit";
import { useEffect } from "react";
import * as React from "react";

import { refundFulfilledStatuses } from "./OrderRefundPage";

export enum OrderRefundType {
  MISCELLANEOUS = "miscellaneous",
  PRODUCTS = "products",
}
export enum OrderRefundAmountCalculationMode {
  AUTOMATIC = "automatic",
  MANUAL = "manual",
  NONE = "none",
}

interface OrderRefundData {
  amount: string;
  type: OrderRefundType;
  refundShipmentCosts: boolean;
  amountCalculationMode: OrderRefundAmountCalculationMode;
}

interface OrderRefundHandlers {
  changeRefundedProductQuantity: FormsetChange<string>;
  setMaximalRefundedProductQuantities: () => void;
  changeRefundedFulfilledProductQuantity: FormsetChange<string>;
  setMaximalRefundedFulfilledProductQuantities: (fulfillmentId: string) => void;
}

export interface OrderRefundFormData extends OrderRefundData {
  refundedProductQuantities: FormsetData<null, string>;
  refundedFulfilledProductQuantities: FormsetData<null, string>;
}

export type OrderRefundSubmitData = OrderRefundFormData;

interface UseOrderRefundFormResult
  extends CommonUseFormResultWithHandlers<OrderRefundFormData, OrderRefundHandlers> {
  disabled: boolean;
}

interface OrderRefundFormProps {
  children: (props: UseOrderRefundFormResult) => React.ReactNode;
  order: OrderRefundDataQuery["order"];
  defaultType: OrderRefundType;
  onSubmit: (data: OrderRefundSubmitData) => SubmitPromise;
  disabled: boolean;
}

function getOrderRefundPageFormData(defaultType: OrderRefundType): OrderRefundData {
  return {
    amount: undefined,
    amountCalculationMode: OrderRefundAmountCalculationMode.NONE,
    refundShipmentCosts: false,
    type: defaultType,
  };
}

function useOrderRefundForm(
  order: OrderRefundDataQuery["order"],
  defaultType: OrderRefundType,
  onSubmit: (data: OrderRefundSubmitData) => SubmitPromise,
  disabled: boolean,
): UseOrderRefundFormResult {
  const {
    handleChange,
    triggerChange,
    data: formData,
    formId,
    setIsSubmitDisabled,
  } = useForm(getOrderRefundPageFormData(defaultType), undefined, {
    confirmLeave: true,
  });
  const { setExitDialogSubmitRef } = useExitFormDialog({
    formId,
  });
  const refundedProductQuantities = useFormset<null, string>(
    order?.lines
      .filter(line => line.quantityToFulfill > 0)
      .map(line => ({
        data: null,
        id: line.id,
        label: null,
        value: "0",
      })),
  );
  const refundedFulfilledProductQuantities = useFormset<null, string>(
    order?.fulfillments
      .filter(({ status }) => refundFulfilledStatuses.includes(status))
      .reduce(
        (linesQty, fulfillemnt) =>
          linesQty.concat(
            fulfillemnt.lines.map(fulfillmentLine => ({
              data: null,
              id: fulfillmentLine.id,
              label: null,
              value: "0",
            })),
          ),
        [],
      ),
  );
  const handleRefundedProductQuantityChange: FormsetChange<string> = (id, value) => {
    triggerChange();
    refundedProductQuantities.change(id, value);
  };
  const handleRefundedFulFilledProductQuantityChange = (id: string, value: string) => {
    triggerChange();
    refundedFulfilledProductQuantities.change(id, value);
  };
  const handleMaximalRefundedProductQuantitiesSet = () => {
    const newQuantities: FormsetData<null, string> = refundedProductQuantities.data.map(
      selectedLine => {
        const line = order.lines.find(line => line.id === selectedLine.id);

        return {
          data: null,
          id: line.id,
          label: null,
          value: line.quantityToFulfill.toString(),
        };
      },
    );

    refundedProductQuantities.set(newQuantities);
    triggerChange();
  };
  const handleMaximalRefundedFulfilledProductQuantitiesSet = (fulfillmentId: string) => {
    const fulfillment = order.fulfillments.find(fulfillment => fulfillment.id === fulfillmentId);
    const newQuantities: FormsetData<null, string> = refundedFulfilledProductQuantities.data.map(
      selectedLine => {
        const line = fulfillment.lines.find(line => line.id === selectedLine.id);

        if (line) {
          return {
            data: null,
            id: line.id,
            label: null,
            value: line.quantity.toString(),
          };
        }

        return selectedLine;
      },
    );

    refundedFulfilledProductQuantities.set(newQuantities);
    triggerChange();
  };
  const data: OrderRefundFormData = {
    ...formData,
    refundedFulfilledProductQuantities: refundedFulfilledProductQuantities.data,
    refundedProductQuantities: refundedProductQuantities.data,
  };
  const handleFormSubmit = useHandleFormSubmit({
    formId,
    onSubmit,
  });
  const submit = () => handleFormSubmit(data);

  useEffect(() => setExitDialogSubmitRef(submit), [submit]);

  const isSaveDisabled = disabled || !order;

  setIsSubmitDisabled(isSaveDisabled);

  return {
    change: handleChange,
    data,
    disabled,
    handlers: {
      changeRefundedFulfilledProductQuantity: handleRefundedFulFilledProductQuantityChange,
      changeRefundedProductQuantity: handleRefundedProductQuantityChange,
      setMaximalRefundedFulfilledProductQuantities:
        handleMaximalRefundedFulfilledProductQuantitiesSet,
      setMaximalRefundedProductQuantities: handleMaximalRefundedProductQuantitiesSet,
    },
    submit,
    isSaveDisabled,
  };
}

const OrderRefundForm = ({
  children,
  order,
  defaultType,
  onSubmit,
  disabled,
}: OrderRefundFormProps) => {
  const props = useOrderRefundForm(order, defaultType, onSubmit, disabled);

  return <form onSubmit={props.submit}>{children(props)}</form>;
};

OrderRefundForm.displayName = "OrderRefundForm";
export default OrderRefundForm;
